package com.tuanzi.loan.workflow.repository.impl;

import static com.tuanzi.loan.generated.jooq.loan.tables.ActHiProcinst.ACT_HI_PROCINST;
import static com.tuanzi.loan.generated.jooq.loan.tables.ActHiTaskinst.ACT_HI_TASKINST;
import static com.tuanzi.loan.generated.jooq.loan.tables.ActReProcdef.ACT_RE_PROCDEF;
import static com.tuanzi.loan.generated.jooq.loan.tables.LoanSystemUser.LOAN_SYSTEM_USER;

import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.jooq.Record;
import org.jooq.RecordMapper;
import org.jooq.impl.DSL;
import org.jooq.impl.TableImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import com.google.common.collect.Lists;
import com.tuanzi.loan.business.repository.system.RoleRepository;
import com.tuanzi.loan.core.repository.JOOQBaseRepository;
import com.tuanzi.loan.workflow.repository.custom.HistoryTaskRepositoryCustom;
import com.tuanzi.loan.workflow.vo.ProcessDefinitionResponse;
import com.tuanzi.loan.workflow.vo.TaskSearchRequest;
import com.tuanzi.loan.workflow.vo.TaskSearchResponse;

/**
 * @author Ray
 * @date 2018/01/12
 */
public class HistoryTaskRepositoryImpl extends JOOQBaseRepository implements HistoryTaskRepositoryCustom {

	@Autowired
	private RoleRepository roleRepository;

	@Override
	protected TableImpl getTable() {
		return ACT_HI_TASKINST;
	}

	@Override
	public Page<TaskSearchResponse> listDoneTask(TaskSearchRequest request, Pageable pageable) {
		fields(ACT_HI_TASKINST.ID_, ACT_HI_TASKINST.PROC_INST_ID_, ACT_HI_TASKINST.PROC_DEF_ID_, ACT_HI_TASKINST.TASK_DEF_KEY_, ACT_HI_TASKINST.NAME_, ACT_RE_PROCDEF.NAME_,
				ACT_RE_PROCDEF.CATEGORY_, ACT_HI_PROCINST.START_TIME_, ACT_HI_PROCINST.NAME_, ACT_HI_PROCINST.START_USER_ID_, ACT_HI_PROCINST.BUSINESS_KEY_, LOAN_SYSTEM_USER.NAME,
				ACT_HI_TASKINST.START_TIME_, ACT_HI_TASKINST.END_TIME_, ACT_HI_TASKINST.DURATION_);

		innerJoin(ACT_RE_PROCDEF, ACT_HI_TASKINST.PROC_DEF_ID_.eq(ACT_RE_PROCDEF.ID_));
		innerJoin(ACT_HI_PROCINST, ACT_HI_TASKINST.PROC_INST_ID_.eq(ACT_HI_PROCINST.ID_));
		innerJoin(LOAN_SYSTEM_USER, ACT_HI_PROCINST.START_USER_ID_.eq(LOAN_SYSTEM_USER.ACCOUNT));

		eq(ACT_HI_TASKINST.ASSIGNEE_, request.getAccount());
		eq(ACT_RE_PROCDEF.CATEGORY_, request.getCategory());
		contains(ACT_RE_PROCDEF.NAME_, request.getProcDefName());
		contains(ACT_HI_PROCINST.NAME_, request.getProcInstName());
		ge(ACT_HI_PROCINST.START_TIME_, request.getProcessInitialStartTime());
		le(ACT_HI_PROCINST.START_TIME_, request.getProcessInitialEndTime());
		isNotNull(ACT_HI_TASKINST.END_TIME_);
		isNotNull(ACT_HI_TASKINST.DELETE_REASON_);
		orderBy(ACT_HI_TASKINST.END_TIME_, Sort.Direction.DESC);
		return search(pageable, new TaskSearchResponseRecordMapper());
	}

	@Override
	public Page<ProcessDefinitionResponse> listStartableTask(TaskSearchRequest request) {

		List<Object> parameters = Lists.newArrayList();
		String sql = "SELECT DISTINCT RES.ID_ AS ID, RES.DEPLOYMENT_ID_ AS DEPLOYMENT_ID, RES.NAME_ AS PROC_DEF_NAME, RES.CATEGORY_ AS CATEGORY, SP.URL AS START_PAGE_URL FROM ACT_RE_PROCDEF RES INNER JOIN act_ext_start_page AS SP ON RES.ID_ = SP.PROC_DEF_ID WHERE RES.VERSION_ = (SELECT max( VERSION_ ) FROM ACT_RE_PROCDEF WHERE KEY_ = RES.KEY_ AND ( ( TENANT_ID_ IS NOT NULL AND TENANT_ID_ = RES.TENANT_ID_ ) OR ( TENANT_ID_ IS NULL AND RES.TENANT_ID_ IS NULL ) ) ) AND (EXISTS ( SELECT ID_ FROM ACT_RU_IDENTITYLINK IDN WHERE IDN.PROC_DEF_ID_ = RES.ID_ AND IDN.USER_ID_ = ?) OR EXISTS ( SELECT ID_ FROM ACT_RU_IDENTITYLINK IDN WHERE IDN.PROC_DEF_ID_ = RES.ID_ AND IDN.GROUP_ID_ IN (?) ) ) ";

		parameters.add(request.getAccount());
		parameters.add(DSL.list(roleRepository.findAllByAccount(request.getAccount()).stream().map(x -> "ROLE_" + x.getCode()).map(DSL::val).collect(Collectors.toList())));

		if (StringUtils.isNotBlank(request.getCategory())) {
			sql += " AND RES.CATEGORY_ = ?";
			parameters.add(request.getCategory());
		}

		if (StringUtils.isNotBlank(request.getProcDefName())) {
			sql += " AND RES.NAME_ LIKE ?";
			parameters.add("%" + request.getProcDefName() + "%");
		}

		sql += " ORDER BY RES.ID_ ASC";

		return new PageImpl<ProcessDefinitionResponse>(dslContext.fetch(sql, parameters.toArray()).map(new RecordMapper<Record, ProcessDefinitionResponse>() {
			@Override
			public ProcessDefinitionResponse map(Record record) {
				ProcessDefinitionResponse response = new ProcessDefinitionResponse();
				response.setId(record.get("ID", String.class));
				response.setName(record.get("PROC_DEF_NAME", String.class));
				response.setCategory(record.get("CATEGORY", String.class));
				response.setStartPageUrl(record.get("START_PAGE_URL", String.class));
				response.setDeploymentId(record.get("DEPLOYMENT_ID", String.class));
				return response;
			}
		}));

	}

	private final class TaskSearchResponseRecordMapper implements RecordMapper<Record, TaskSearchResponse> {

		@Override
		public TaskSearchResponse map(Record record) {
			TaskSearchResponse response = new TaskSearchResponse();
			response.setId(record.get(ACT_HI_TASKINST.ID_));
			response.setProcDefId(record.get(ACT_HI_TASKINST.PROC_DEF_ID_));
			response.setProcInstId(record.get(ACT_HI_TASKINST.PROC_INST_ID_));
			response.setTaskDefKey(record.get(ACT_HI_TASKINST.TASK_DEF_KEY_));
			response.setTaskName(record.get(ACT_HI_TASKINST.NAME_));
			response.setProcDefName(record.get(ACT_RE_PROCDEF.NAME_));
			response.setProcInstStartTime(record.get(ACT_HI_PROCINST.START_TIME_));
			response.setTaskStartTime(record.get(ACT_HI_TASKINST.START_TIME_));
			response.setTaskEndTime(record.get(ACT_HI_TASKINST.END_TIME_));
			response.setProcInstName(record.get(ACT_HI_PROCINST.NAME_));
			response.setStartUserId(record.get(ACT_HI_PROCINST.START_USER_ID_));
			response.setBusinessKey(record.get(ACT_HI_PROCINST.BUSINESS_KEY_));
			response.setCategory(record.get(ACT_RE_PROCDEF.CATEGORY_));
			response.setStartUserName(record.get(LOAN_SYSTEM_USER.NAME));
			response.setTaskDuration(record.get(ACT_HI_TASKINST.DURATION_));
			return response;
		}
	}
}
